package tjs.org.springLoader.fileWatch;

import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import tjs.org.springLoader.reloader.ChangeClassReloader;
import tjs.org.springLoader.reloader.IReloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.List;

public class FileListenerAdaptor extends FileAlterationListenerAdaptor {

    private List<IReloader> iReloaders;
    private ChangeClassReloader changeClassReloader;

    public FileListenerAdaptor(List<IReloader> iReloaders, ChangeClassReloader changeClassReloader) {
        this.iReloaders = iReloaders;
        this.changeClassReloader = changeClassReloader;
    }

    public FileListenerAdaptor() {
    }

    @Override
    public void onStart(FileAlterationObserver arg0) {
        //System.out.println("begin listening!");
    }

    @Override
    public void onStop(FileAlterationObserver arg0) {
        //System.out.println("end listening!");
    }

    @Override
    public void onDirectoryCreate(File fold) {
        System.out.println("fold: " + fold.getAbsolutePath() + " is created.");
    }

    @Override
    public void onDirectoryChange(File fold) {
        System.out.println("fold: " + fold.getAbsolutePath() + " is changed.");
    }

    @Override
    public void onDirectoryDelete(File fold) {
        System.out.println("fold: " + fold.getAbsolutePath() + " is deleted.");
    }

    @Override
    public void onFileCreate(File file) {
        System.out.println("file: " + file.getAbsolutePath() + " is created.");

        reloaderAfter(file);
    }

    @Override
    public void onFileChange(File file) {
        System.out.println("file: " + file.getAbsolutePath() + " is changed.");

        reloaderBefore(file);
        reloaderAfter(file);
    }

    @Override
    public void onFileDelete(File file) {
        System.out.println("file: " + file.getAbsolutePath() + " is deleted");

        reloaderBefore(file);
    }

    public void reloaderBefore(File file) {
        Class<?> oldClass = getOldClass(file);
        for (IReloader iReloader : iReloaders) {
            try {
                if (iReloader.isReloader(oldClass)) {
                    iReloader.reloaderBefore(oldClass);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public void reloaderAfter(File file) {
        Class<?> newClass = getNewClass(file);
        for (IReloader iReloader : iReloaders) {
            try {
                if (iReloader.isReloader(newClass)) {
                    iReloader.reloaderAfter(newClass);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    //todo cache class
    public Class<?> getNewClass(File file) {
        Class<?> resClass = null;
        byte[] bytes = null;
        try {
            FileInputStream inputStream = new FileInputStream(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int b = 0;
            while ((b = inputStream.read()) != -1) {
                baos.write(b);
            }
            inputStream.close();
            bytes = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }

        String className = getClassNameByFile(file.getPath());
        if (bytes == null) {
            resClass = getOldClass(file);
            return resClass;
        }
        resClass = changeClassReloader.defineClass(className, bytes);
        return resClass;
    }


    public Class<?> getOldClass(File file) {
        Class<?> resClass = null;
        String className = getClassNameByFile(file.getPath());
        try {
            resClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return resClass;
    }

    private String getClassNameByFile(String filePath) {
        return filePath.substring(filePath.lastIndexOf("classes") + 8, filePath.indexOf(".class")).replaceAll("/", ".");
    }

}